Objetivo: entender o comportamento de clientes inadimplêntes
base de dados: Dados csv clientes banco stone
baseado : https://www.kaggle.com/code/caiodouglas/analise-de-dados-prevencao-de-inadimplencia
No mundo econômico, a previsibilidade é um fator de extrema importância, já que as tomadas de decisões e investimentos dependem de entender o risco que se corre. Num cenário de concessão de empréstimo, a entidade financeira precisa calcular quais as chances do cliente deixar de efetuar o pagamento, tornando assim inadimplente, prejudicando o investimento.¶ Com a análise de dados dos clientes da entidade financeira, iremos entender quais são as caracteristicas e atitudes que um possível inadimplente possui, levando assim uma melhor tomada de decisão sobre a concessão ou não do crédito. A variável de interesse dos dados é a variável default que mostra se o cliente está ou já foi inadimplente em algum momento, com os valores booleanos 0 para negativo e 1 para positivo.
# IMPORTAR BIBLIOTECAS
import pandas as pd # blioteca para tratar dados
import seaborn as sns # biblioteca de graficos
import matplotlib.pyplot as plt # sns # biblioteca de graficos
import plotly.express as px # sns # biblioteca de graficos
# Carregar base de dados dos clientes da entidade financeira
df = pd.read_csv("Python_M10_support material.csv", na_values= 'na')
display(df)
| id | default | idade | sexo | dependentes | escolaridade | estado_civil | salario_anual | tipo_cartao | meses_de_relacionamento | qtd_produtos | iteracoes_12m | meses_inativo_12m | limite_credito | valor_transacoes_12m | qtd_transacoes_12m | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 768805383 | 0 | 45 | M | 3 | ensino medio | casado | $60K - $80K | blue | 39 | 5 | 3 | 1 | 12.691,51 | 1.144,90 | 42 |
| 1 | 818770008 | 0 | 49 | F | 5 | mestrado | solteiro | menos que $40K | blue | 44 | 6 | 2 | 1 | 8.256,96 | 1.291,45 | 33 |
| 2 | 713982108 | 0 | 51 | M | 3 | mestrado | casado | $80K - $120K | blue | 36 | 4 | 0 | 1 | 3.418,56 | 1.887,72 | 20 |
| 3 | 769911858 | 0 | 40 | F | 4 | ensino medio | NaN | menos que $40K | blue | 34 | 3 | 1 | 4 | 3.313,03 | 1.171,56 | 20 |
| 4 | 709106358 | 0 | 40 | M | 3 | sem educacao formal | casado | $60K - $80K | blue | 21 | 5 | 0 | 1 | 4.716,22 | 816,08 | 28 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10122 | 772366833 | 0 | 50 | M | 2 | mestrado | solteiro | $40K - $60K | blue | 40 | 3 | 3 | 2 | 4.003,91 | 15.476,26 | 117 |
| 10123 | 710638233 | 1 | 41 | M | 2 | NaN | divorciado | $40K - $60K | blue | 25 | 4 | 3 | 2 | 4.277,04 | 8.764,88 | 69 |
| 10124 | 716506083 | 1 | 44 | F | 1 | ensino medio | casado | menos que $40K | blue | 36 | 5 | 4 | 3 | 5.409,16 | 10.291,78 | 60 |
| 10125 | 717406983 | 1 | 30 | M | 2 | mestrado | NaN | $40K - $60K | blue | 36 | 4 | 3 | 3 | 5.281,84 | 8.395,62 | 62 |
| 10126 | 714337233 | 1 | 43 | F | 2 | mestrado | casado | menos que $40K | silver | 25 | 6 | 4 | 2 | 10.388,80 | 10.294,96 | 61 |
10127 rows × 16 columns
# verificar as informações da base de dados
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10127 entries, 0 to 10126 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 10127 non-null int64 1 default 10127 non-null int64 2 idade 10127 non-null int64 3 sexo 10127 non-null object 4 dependentes 10127 non-null int64 5 escolaridade 8608 non-null object 6 estado_civil 9378 non-null object 7 salario_anual 9015 non-null object 8 tipo_cartao 10127 non-null object 9 meses_de_relacionamento 10127 non-null int64 10 qtd_produtos 10127 non-null int64 11 iteracoes_12m 10127 non-null int64 12 meses_inativo_12m 10127 non-null int64 13 limite_credito 10127 non-null object 14 valor_transacoes_12m 10127 non-null object 15 qtd_transacoes_12m 10127 non-null int64 dtypes: int64(9), object(7) memory usage: 1.2+ MB
As colunas escolaridade, estado_civil e salario_anual, estão com dados faltantes. As colubnas limite de credito e valor transações estão formato de object precisa ser alterado para float
# tratar string, limite de credito e valor transações esta como object alterar para float
df["limite_credito"] = df["limite_credito"].apply(lambda valor: float(valor.replace(".", "").replace(",", ".")))
df["valor_transacoes_12m"] = df["valor_transacoes_12m"].apply(lambda valor: float(valor.replace(".", "").replace(",", ".")))
print("-"*60)
df.info()
------------------------------------------------------------ <class 'pandas.core.frame.DataFrame'> RangeIndex: 10127 entries, 0 to 10126 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 10127 non-null int64 1 default 10127 non-null int64 2 idade 10127 non-null int64 3 sexo 10127 non-null object 4 dependentes 10127 non-null int64 5 escolaridade 8608 non-null object 6 estado_civil 9378 non-null object 7 salario_anual 9015 non-null object 8 tipo_cartao 10127 non-null object 9 meses_de_relacionamento 10127 non-null int64 10 qtd_produtos 10127 non-null int64 11 iteracoes_12m 10127 non-null int64 12 meses_inativo_12m 10127 non-null int64 13 limite_credito 10127 non-null float64 14 valor_transacoes_12m 10127 non-null float64 15 qtd_transacoes_12m 10127 non-null int64 dtypes: float64(2), int64(9), object(5) memory usage: 1.2+ MB
# contagem de dados antes da limpeza de valores vazio
print("contar numero de inadimplentes (numero 1) e adiplentes (numero 0)")
print(df["default"].value_counts())
print("-"*60)
print("contar numero de inadimplentes (numero 1) e adiplentes (numero 0) em porcetagem (%)")
print(df["default"].value_counts(normalize=True).map("{:.1%}".format))
print("-"*60)
print("Quantidade total de clientes com dados para analisar")
print(len(df['id']))
print("-"*60)
contar numero de inadimplentes (numero 1) e adiplentes (numero 0) 0 8500 1 1627 Name: default, dtype: int64 ------------------------------------------------------------ contar numero de inadimplentes (numero 1) e adiplentes (numero 0) em porcetagem (%) 0 83.9% 1 16.1% Name: default, dtype: object ------------------------------------------------------------ Quantidade total de clientes com dados para analisar 10127 ------------------------------------------------------------
Com tudo é notorio que há um total de dados de 10127 clientes, para analise o foco principal sera a coluna default onde mostra se o cliente é adiplente ou inadimplentes, sendo assim o numero 0 para adiplentes e 1 para inadimplentes. Dos 101227 clientes 8500 sendo 83,9% representa os clientes adiplentes e 1627 contando 16,1% rempresenta os clientes inadimplentes.O intuito é analisar oque tem em comum ou em relação para diminuir esses 16,1% sendo que para entidade finaceira a porcetagem está muito alta e representa uma grande poblema para a empresa.
# valores faltantes
print(df.isnull().sum())
print("-"*60)
id 0 default 0 idade 0 sexo 0 dependentes 0 escolaridade 1519 estado_civil 749 salario_anual 1112 tipo_cartao 0 meses_de_relacionamento 0 qtd_produtos 0 iteracoes_12m 0 meses_inativo_12m 0 limite_credito 0 valor_transacoes_12m 0 qtd_transacoes_12m 0 dtype: int64 ------------------------------------------------------------
# deletar valores faltantes
df = df.dropna()
df.info()
# contagem de dados depois da limpeza de valores vazio
print("-"*60)
print("contar numero de inadimplentes (numero 1) e adiplentes (numero 0)")
print(df["default"].value_counts())
print("-"*60)
print("contar numero de inadimplentes (numero 1) e adiplentes (numero 0) em porcetagem (%)")
print(df["default"].value_counts(normalize=True).map("{:.1%}".format))
print("-"*60)
print("Quantidade total de clientes com dados para analisar")
print(len(df['id']))
print("-"*60)
<class 'pandas.core.frame.DataFrame'> Int64Index: 7081 entries, 0 to 10126 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 7081 non-null int64 1 default 7081 non-null int64 2 idade 7081 non-null int64 3 sexo 7081 non-null object 4 dependentes 7081 non-null int64 5 escolaridade 7081 non-null object 6 estado_civil 7081 non-null object 7 salario_anual 7081 non-null object 8 tipo_cartao 7081 non-null object 9 meses_de_relacionamento 7081 non-null int64 10 qtd_produtos 7081 non-null int64 11 iteracoes_12m 7081 non-null int64 12 meses_inativo_12m 7081 non-null int64 13 limite_credito 7081 non-null float64 14 valor_transacoes_12m 7081 non-null float64 15 qtd_transacoes_12m 7081 non-null int64 dtypes: float64(2), int64(9), object(5) memory usage: 940.4+ KB ------------------------------------------------------------ contar numero de inadimplentes (numero 1) e adiplentes (numero 0) 0 5968 1 1113 Name: default, dtype: int64 ------------------------------------------------------------ contar numero de inadimplentes (numero 1) e adiplentes (numero 0) em porcetagem (%) 0 84.3% 1 15.7% Name: default, dtype: object ------------------------------------------------------------ Quantidade total de clientes com dados para analisar 7081 ------------------------------------------------------------
Após a limpeza perdemos cerca de 3046 dados de clientes por valores faltantes, pois iria afetar a analise
# deletar coluna que não tem valor relevante, nesta estapa o numero do id e O sexo é irelevante para analise
df = df.drop(columns=["id", "sexo"])
# Exibir nova base de dados tratada
display(df)
| default | idade | dependentes | escolaridade | estado_civil | salario_anual | tipo_cartao | meses_de_relacionamento | qtd_produtos | iteracoes_12m | meses_inativo_12m | limite_credito | valor_transacoes_12m | qtd_transacoes_12m | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 45 | 3 | ensino medio | casado | $60K - $80K | blue | 39 | 5 | 3 | 1 | 12691.51 | 1144.90 | 42 |
| 1 | 0 | 49 | 5 | mestrado | solteiro | menos que $40K | blue | 44 | 6 | 2 | 1 | 8256.96 | 1291.45 | 33 |
| 2 | 0 | 51 | 3 | mestrado | casado | $80K - $120K | blue | 36 | 4 | 0 | 1 | 3418.56 | 1887.72 | 20 |
| 4 | 0 | 40 | 3 | sem educacao formal | casado | $60K - $80K | blue | 21 | 5 | 0 | 1 | 4716.22 | 816.08 | 28 |
| 5 | 0 | 44 | 2 | mestrado | casado | $40K - $60K | blue | 36 | 3 | 2 | 1 | 4010.69 | 1088.07 | 24 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 10120 | 0 | 54 | 1 | ensino medio | solteiro | $60K - $80K | blue | 34 | 5 | 0 | 2 | 13940.62 | 15577.67 | 114 |
| 10121 | 0 | 56 | 1 | mestrado | solteiro | menos que $40K | blue | 50 | 4 | 4 | 1 | 3688.95 | 14596.49 | 120 |
| 10122 | 0 | 50 | 2 | mestrado | solteiro | $40K - $60K | blue | 40 | 3 | 3 | 2 | 4003.91 | 15476.26 | 117 |
| 10124 | 1 | 44 | 1 | ensino medio | casado | menos que $40K | blue | 36 | 5 | 4 | 3 | 5409.16 | 10291.78 | 60 |
| 10126 | 1 | 43 | 2 | mestrado | casado | menos que $40K | silver | 25 | 6 | 4 | 2 | 10388.80 | 10294.96 | 61 |
7081 rows × 14 columns
# DADOS estatístico string object
df.select_dtypes('object').describe().transpose()
| count | unique | top | freq | |
|---|---|---|---|---|
| escolaridade | 7081 | 5 | mestrado | 2591 |
| estado_civil | 7081 | 3 | casado | 3564 |
| salario_anual | 7081 | 5 | menos que $40K | 2792 |
| tipo_cartao | 7081 | 4 | blue | 6598 |
# dados estatístico numerico
df.select_dtypes('number').describe().transpose()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| default | 7081.0 | 0.157181 | 0.363997 | 0.00 | 0.00 | 0.00 | 0.00 | 1.00 |
| idade | 7081.0 | 46.347691 | 8.041225 | 26.00 | 41.00 | 46.00 | 52.00 | 73.00 |
| dependentes | 7081.0 | 2.337805 | 1.291649 | 0.00 | 1.00 | 2.00 | 3.00 | 5.00 |
| meses_de_relacionamento | 7081.0 | 35.981359 | 8.002609 | 13.00 | 31.00 | 36.00 | 40.00 | 56.00 |
| qtd_produtos | 7081.0 | 3.819376 | 1.544444 | 1.00 | 3.00 | 4.00 | 5.00 | 6.00 |
| iteracoes_12m | 7081.0 | 2.454456 | 1.104917 | 0.00 | 2.00 | 2.00 | 3.00 | 6.00 |
| meses_inativo_12m | 7081.0 | 2.342607 | 0.995104 | 0.00 | 2.00 | 2.00 | 3.00 | 6.00 |
| limite_credito | 7081.0 | 8493.259524 | 9126.082105 | 1438.00 | 2498.35 | 4287.22 | 10729.40 | 34516.99 |
| valor_transacoes_12m | 7081.0 | 4394.800669 | 3468.457771 | 510.16 | 2089.63 | 3831.09 | 4740.51 | 17995.49 |
| qtd_transacoes_12m | 7081.0 | 64.503319 | 23.809330 | 10.00 | 44.00 | 67.00 | 80.00 | 134.00 |
Pode-se analisar no primeiro grafico estatístico informações como quantas variveis tem em cada colunas, a variavel que mais se repete e qual a frequencia que mais se repetiu. No entanto ja no segundo grafico podemos analisar informações como o minimo ou o maximo das variaveis.
# Analise com graficos comparativos com coluna Default contudo essa analise usar apenas os dados dos 7081 clientes.
for coluna in df.columns:
grafico = px.histogram(df, x=coluna, color="default", text_auto=True, color_discrete_sequence=["blue", "orange"])
grafico.show()
# Analise com Salario anual
print(df["salario_anual"].value_counts())
print("-"*60)
# Alterar para numero para poder usar na analise
df["salario_anual"] = df["salario_anual"].replace("menos que $40K", "0")
df["salario_anual"] = df["salario_anual"].replace("$40K - $60K", "1")
df["salario_anual"] = df["salario_anual"].replace("$60K - $80K", "2")
df["salario_anual"] = df["salario_anual"].replace("$80K - $120K", "3")
df["salario_anual"] = df["salario_anual"].replace("$120K +", "4")
print(df["salario_anual"].value_counts())
# Passar coluna Salario anual para numero inteiro
df['salario_anual'] = df['salario_anual'].astype(int)
print("-"*60)
df.info()
menos que $40K 2792 $40K - $60K 1412 $80K - $120K 1202 $60K - $80K 1103 $120K + 572 Name: salario_anual, dtype: int64 ------------------------------------------------------------ 0 2792 1 1412 3 1202 2 1103 4 572 Name: salario_anual, dtype: int64 ------------------------------------------------------------ <class 'pandas.core.frame.DataFrame'> Int64Index: 7081 entries, 0 to 10126 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 default 7081 non-null int64 1 idade 7081 non-null int64 2 dependentes 7081 non-null int64 3 escolaridade 7081 non-null object 4 estado_civil 7081 non-null object 5 salario_anual 7081 non-null int32 6 tipo_cartao 7081 non-null object 7 meses_de_relacionamento 7081 non-null int64 8 qtd_produtos 7081 non-null int64 9 iteracoes_12m 7081 non-null int64 10 meses_inativo_12m 7081 non-null int64 11 limite_credito 7081 non-null float64 12 valor_transacoes_12m 7081 non-null float64 13 qtd_transacoes_12m 7081 non-null int64 dtypes: float64(2), int32(1), int64(8), object(3) memory usage: 802.1+ KB
# Analise com Estado civil
print(df["estado_civil"].value_counts())
print("-"*60)
# Alterar para numero para poder usar na analise
df["estado_civil"] = df["estado_civil"].replace("casado", "0")
df["estado_civil"] = df["estado_civil"].replace("solteiro", "1")
df["estado_civil"] = df["estado_civil"].replace("divorciado", "2")
print(df["estado_civil"].value_counts())
# Passar coluna Salario anual para numero inteiro
df['estado_civil'] = df['estado_civil'].astype(int)
print("-"*60)
df.info()
casado 3564 solteiro 2948 divorciado 569 Name: estado_civil, dtype: int64 ------------------------------------------------------------ 0 3564 1 2948 2 569 Name: estado_civil, dtype: int64 ------------------------------------------------------------ <class 'pandas.core.frame.DataFrame'> Int64Index: 7081 entries, 0 to 10126 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 default 7081 non-null int64 1 idade 7081 non-null int64 2 dependentes 7081 non-null int64 3 escolaridade 7081 non-null object 4 estado_civil 7081 non-null int32 5 salario_anual 7081 non-null int32 6 tipo_cartao 7081 non-null object 7 meses_de_relacionamento 7081 non-null int64 8 qtd_produtos 7081 non-null int64 9 iteracoes_12m 7081 non-null int64 10 meses_inativo_12m 7081 non-null int64 11 limite_credito 7081 non-null float64 12 valor_transacoes_12m 7081 non-null float64 13 qtd_transacoes_12m 7081 non-null int64 dtypes: float64(2), int32(2), int64(8), object(2) memory usage: 774.5+ KB
# analise escolaridade
print(df["escolaridade"].value_counts())
print("-"*60)
df["escolaridade"] = df["escolaridade"].replace("mestrado", "0")
df["escolaridade"] = df["escolaridade"].replace("ensino medio", "1")
df["escolaridade"] = df["escolaridade"].replace("sem educacao formal", "2")
df["escolaridade"] = df["escolaridade"].replace("graduacao", "3")
df["escolaridade"] = df["escolaridade"].replace("doutorado", "4")
print(df["escolaridade"].value_counts())
# Passar coluna Salario anual para numero inteiro
df['escolaridade'] = df['escolaridade'].astype(int)
print("-"*60)
df.info()
mestrado 2591 ensino medio 1653 sem educacao formal 1204 graduacao 844 doutorado 789 Name: escolaridade, dtype: int64 ------------------------------------------------------------ 0 2591 1 1653 2 1204 3 844 4 789 Name: escolaridade, dtype: int64 ------------------------------------------------------------ <class 'pandas.core.frame.DataFrame'> Int64Index: 7081 entries, 0 to 10126 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 default 7081 non-null int64 1 idade 7081 non-null int64 2 dependentes 7081 non-null int64 3 escolaridade 7081 non-null int32 4 estado_civil 7081 non-null int32 5 salario_anual 7081 non-null int32 6 tipo_cartao 7081 non-null object 7 meses_de_relacionamento 7081 non-null int64 8 qtd_produtos 7081 non-null int64 9 iteracoes_12m 7081 non-null int64 10 meses_inativo_12m 7081 non-null int64 11 limite_credito 7081 non-null float64 12 valor_transacoes_12m 7081 non-null float64 13 qtd_transacoes_12m 7081 non-null int64 dtypes: float64(2), int32(3), int64(8), object(1) memory usage: 746.8+ KB
sns.pairplot(df)
plt.show()
# verificar correlação com um mapa de calor
plt.figure(figsize=(15, 10))
sns.heatmap(df.corr(), annot=True, cmap='Oranges')
<AxesSubplot:>
# Analise com graficos comparativos com coluna Default contudo essa analise usar apenas os dados dos 7081 clientes.
for coluna in df.columns:
grafico = px.histogram(df, x=coluna, color="default", text_auto=True, color_discrete_sequence=['Blue', 'Orange'])
grafico.show()
grafico1 = sns.relplot(x='valor_transacoes_12m', y='qtd_transacoes_12m', data=df, hue='default')
grafico2 = sns.relplot(y='qtd_transacoes_12m', x='meses_de_relacionamento', data=df, hue='default')
grafico3 = sns.relplot(x='qtd_transacoes_12m', y='limite_credito', data=df, hue='default')
grafico4 = sns.relplot(y='salario_anual', x='valor_transacoes_12m', data=df, hue='default')
No grafico 1 podemos notar que quanto maior o valor das transações e quanto mais transações menor a chance de inadimplência.
No grafico 2 nota-se que no intervalo de meses de 25 a 45 nesse intervalo de 30 meses ha um grande numero de inadimplência.
No grafico 3 nos informa que quanto menor o limite de credito mais chance de inadimpência.
Com analise dos salario dos clientes podemos ter uma relação com a inadimplência como por exempo salario menos de 60 k tem a maior probabilidade de ter inadimplência, logo em seguinda de 80 a 120 k.
Essas analises é importe para um mapeamento para uma movimentação precisa para diminuir os 16,10% de inadimplência.